docs: add Security and Governance and compliance pages - #850
Conversation
The published documentation had no security page. What existed was a four-paragraph "Security" section inside Platform Support, covering per-trust API keys and payload encryption and nothing else — no mention of MFA, RBAC, the cohort-query boundary, the cloud posture, FL transport security, or the supply chain. A partner trust assessing FLIP had nowhere to look. Adds `security.rst` at the end of the Documentation toctree, describing the controls layer by layer: network and perimeter, cloud infrastructure, identity and access, the clinical data boundary, federated learning, data in transit and at rest, disclosure control in error paths, supply chain and change control, and how to report a vulnerability. Removes the old section from Platform Support. Beyond being superseded, it had gone stale: it stated that payloads are encrypted with a shared `AES_KEY_BASE64`, which no longer describes the scheme. Platform Support keeps Networking, Backup/Restore and Access, which are operator-facing rather than assurance-facing. Two things are stated deliberately rather than glossed, because the audience is information governance: - The FL update filter is described as statistical clipping and sparsification, and explicitly **not** as differential privacy — there is no calibrated noise and no privacy budget. Presenting it as DP to an ethics committee would misstate it. - Researcher-supplied training code runs on trust hardware with access to that trust's data. That is inherent to federated learning, so the page says so plainly and then sets out the controls around it. The page carries no file paths, line numbers, or internal finding identifiers, so it is safe to share with external stakeholders. Cross-references the existing on-prem deployment guide for trust firewall rules and the RBAC role matrix for the full permission table. Sphinx builds clean — the eight remaining warnings are pre-existing autoapi issues in generated files, unrelated to this page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude <noreply@anthropic.com>
Two follow-ups from review of the new Security page. **User Roles.** The roles sentence now links to the permission matrix at the point the three roles are named, rather than deferring to a pointer at the end of the paragraph — a reader who wants the detail is looking for it right there. Uses the existing `rbac-roles` cross-reference so it resolves in every build, including the PR preview. **Networking in Platform Support** repeated the outbound-only posture three times across the section and again in the ports preamble, most of which the Security page now covers. Trimmed to one paragraph of substance plus a pointer, keeping everything operational: the architecture figure, the ports table, the SSM port-forwarding command, and the fact that port 22 is never opened. The VPN paragraph also said the tunnel "is not yet implemented — current deployments rely on HTTPS over the public internet — but is planned as part of the production rollout", which no longer matches: it is provisioned on request. Reworded accordingly, and phrased so it does not imply every deployment has one. Sphinx builds clean; verified both new cross-links resolve in the rendered HTML and the ports table is intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude <noreply@anthropic.com>
The docs build carried eight warnings, all from docstrings that autoapi renders as reStructuredText but which were written as if they were Markdown. Three source docstrings account for all eight — the constants one is counted twice because autoapi emits both a module page and a package page from it. - `flip/constants/flip_constants.py` — a bullet list with no blank line before it, so RST read the continuation line as an unexpected indent inside a paragraph (*Unexpected indentation*, *Block quote ends without a blank line*). Added the blank line; it now renders as an actual list. - `flip/flower/metrics.py` — an indented usage example after a plain `:` made the preceding line a definition-list term (*Definition list ends without a blank line*). Changed to `::` so it is an explicit literal block. - `flip_api/scripts/delete_trust.py` — a Markdown pipe table. RST parses leading `|` as a line block and reads `|---...---|` as a substitution reference, which produced the two *Undefined substitution referenced* errors and the *Line block ends without a blank line* warning. Moved into a literal block, which keeps the table readable both in the source and on the rendered page. All are comment-only changes; no behaviour is affected. `make -C docs docs` now reports **build succeeded** with no warnings, and the three sites were checked in the rendered HTML rather than only in the build log. Verified: ruff clean on all three files, mypy clean on `delete_trust.py`, and the flip-api tests covering it pass. flip-utils' suite could not run here — its dependencies are not installed in this environment and cannot be fetched — but both files parse and ruff is clean, and autoapi re-parsed them to regenerate the docs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Companion to the Security page, at the end of the Documentation toctree. Security answers "what protects this"; this answers "who decides, and how does it map onto the frameworks we are assessed against" — a different question, asked by a different reader (information governance and Caldicott functions, ethics committees, commissioners). Covers the governance properties that are not obvious from the code: - **Per-project, per-trust veto.** Approval is not a blanket agreement to participate. A trust approves each project individually, the decision runs on its own deployment so it cannot be bypassed, and declining excludes that trust without affecting the rest of the federation. - **Data residency** — what actually crosses the boundary in each direction, stated explicitly, since it is the first question an IG function asks. - **National Data Opt-Out** — applied by periodic reconciliation of the OMOP dataset rather than in the query layer, so it propagates to every query the platform can make. Two caveats are recorded rather than glossed: reconciliation is periodic rather than per-query, so the interval is the control question; and the platform inherits the decision rather than enforcing it. - **Network architecture as a governance guarantee** — kept short and pointed at the Security page for the mechanism, rather than restating it. - Access governance and independent assurance. Three appendices map the platform against **Cyber Essentials**, the **NHS DSPT** (via the National Data Guardian's ten standards), and the **NCSC Cyber Assessment Framework**, as `list-table`s with an explicit status column. Statuses distinguish controls that are the platform's from those that belong to the deploying trust, and from those that are shared — an all-green mapping would be misleading, since several of these standards are genuinely organisational. Cross-references Security, User Roles, and the on-prem deployment guide rather than duplicating them. Sphinx builds clean with no warnings; verified the six tables render and all three cross-links resolve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude <noreply@anthropic.com>
… threshold Address review: the security page described per-trust authenticated payload encryption (key identifiers, AEAD) and the correlation-ID error mechanism with its CI guard as current behaviour; both are in-flight work. Rewritten to state today's shared-key scheme and error paths precisely, with the upgrades marked as in delivery. The rotation paragraph and the CAF B3 appendix row are aligned to match. Also state that the cohort suppression threshold is the trust's own disclosure floor, set per trust in its deployment kit, on both the security page and the governance page. Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
garciadias
left a comment
There was a problem hiding this comment.
These two new pages are well-written and thoroughly researched — I independently re-verified the checkable technical claims (MFA enforcement, RDS Proxy IAM auth, the sqlglot-based query validation, the AES single-key model, WAF monitoring-mode status, constant-time credential comparisons) and all of them match the current implementation, and the Sphinx build is clean.
One real issue, though: both pages state as present-tense fact that each trust sets its own cohort-suppression threshold in its deployment kit. That isn't true on develop yet — COHORT_QUERY_THRESHOLD is currently a hardcoded default that no trust kit or compose file actually wires up, which is exactly what PR #839 (open at time of review) delivers. Requesting changes so this doesn't ship a false compliance claim to IG/Caldicott readers if it merges ahead of #839 — either hold this PR until #839 lands, or soften the wording to "in delivery" in the meantime (see inline comments).
Description
The published documentation had no security page and no governance page. What existed was a four-paragraph Security section inside Platform Support, covering per-trust API keys and payload encryption and nothing else — no MFA, no RBAC, no cohort-query boundary, no cloud posture, no FL transport security, no supply chain, and nothing at all on information governance. A partner trust assessing FLIP had nowhere to look.
This adds two top-level pages at the end of the Documentation toctree, fixes the stale section they supersede, and clears every pre-existing warning from the docs build.
1.
security.rst— what protects the platformDescribes the controls layer by layer:
SECURITY.md2.
governance-and-compliance.rst— who decides, and how we map to the frameworksA different question for a different reader (information governance and Caldicott functions, ethics committees, commissioners):
Statuses distinguish controls that are the platform's from those belonging to the deploying trust, and from those that are shared. An all-green mapping would be misleading — several of these standards are genuinely organisational.
3. Platform Support trimmed
The old Security section is removed as superseded — its content (per-trust API keys, shared-key payload encryption) is covered in far more detail on the new page.
Networking repeated the outbound-only posture three times across the section and again in the ports preamble. Trimmed to one paragraph plus a pointer to Security, keeping everything operational — the architecture figure, the full firewall-rules table,
make forward-trust, and "port 22 is never opened". Its VPN paragraph said the tunnel "is not yet implemented ... but is planned as part of the production rollout", which no longer matches; reworded to "provisioned on request rather than by default".4. Docs build is now warning-free
The build previously emitted eight warnings, all from docstrings written as if they were Markdown. Three source docstrings accounted for all eight (the constants one counted twice, because autoapi emits both a module and a package page):
flip/constants/flip_constants.py— bullet list with no preceding blank line → Unexpected indentation / Block quote ends without a blank lineflip/flower/metrics.py— indented example after a plain:made the previous line a definition-list term → changed to::flip_api/scripts/delete_trust.py— a Markdown pipe table; RST parses leading|as a line block and|---...---|as a substitution reference → moved into a literal blockAll comment-only; no behaviour affected.
make -C docs docsnow reports build succeeded with no warnings.Stated deliberately rather than glossed
Because the audience is information governance, the pages mark the line between shipped controls and in-flight work explicitly (the last two tightened in the review round):
Neither page carries file paths, line numbers, or internal finding identifiers, so both are safe to share with external stakeholders.
Linked Issues
Checklist
Type of Change
make -C docs/ docs.Testing
make -C docs docsbuilds clean, zero warnings (previously eight), re-verified after the review-round changes.list-tables in the appendices render as tables, both pages appear in the Documentation sidebar in the right order, and the old#securityanchor is gone fromadmin-platform-support.html.:ref:security, `:ref:`rbac-roles,:ref:deploy-flip-node-on-prem``.ruffclean on the three touched Python files;mypyclean ondelete_trust.py; the flip-api tests covering it pass.Not verified here: flip-utils' own test suite could not run in this environment (its dependencies are not installed and cannot be fetched). Both touched files parse and are ruff-clean, and autoapi re-parsed them to regenerate the API docs, but the suite itself has not run against these changes. They are docstring-only edits.
Additional Notes
Sibling pages use sentence-case titles without a shared prefix (Overview, FLIP components, System administration), so the pages are titled Security and Governance and compliance. "Security" is what a reader scans the sidebar for, and neither title dates the way "Security Review" would as the platform evolves.
The per-trust threshold wording depended on the kit-file plumbing from #839 (
COHORT_QUERY_THRESHOLDin the trust kit). #839 merged on 2026-08-05 (01292201), so that ordering constraint is discharged and both passages now describe shipped behaviour: the setting ships in the GSTT/KCH/generic kit examples, reaches data-access-api via${COHORT_QUERY_THRESHOLD:-10}in bothcompose_trust.development.ymlandcompose_trust.production.yml, and themax(threshold, get_settings().COHORT_QUERY_THRESHOLD)clamp indata_access_api/services/cohort.pymeans a hub-supplied value can only raise the floor. The encryption and correlation-identifier paragraphs stay marked in delivery — #845 is still open.Commit 4 of 4 touches
flip-utils/andflip-api/rather thandocs/. It is documentation-only in effect — three docstrings — but happy to split it out if you would rather keep the PR todocs/.